///
/// [Author] Alex (https://github.com/AlexV525)
/// [Date] 2020-03-11 09:53
///
import 'dart:async';
import 'dart:io';
import 'dart:math' as math;

import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:extended_image/extended_image.dart';
import 'package:flutterlib/common/aliyun/aliyun_oss.dart';
import 'package:flutterlib/common/constants/constants.dart';
import 'package:flutterlib/common/net/retrofit/post_api.dart';
import 'package:flutterlib/common/net/retrofit/user_api.dart';
import 'package:flutterlib/common/style/meterial_design_icons.dart';
import 'package:flutterlib/common/utils/utils.dart';
import 'package:flutterlib/model/post/post_create_bean.dart';
import 'package:flutterlib/widgets/fixed_appbar.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:thrio/thrio.dart';
import 'package:wechat_assets_picker/wechat_assets_picker.dart';

class Page extends StatefulWidget {
  final int interestId;

  const Page({Key key, this.interestId}) : super(key: key);
  @override
  _PageState createState() => _PageState();
}

class _PageState extends State<Page> with AutomaticKeepAliveClientMixin {
  final OSSAuthCredentialProvider _provider = OSSAuthCredentialProvider.init(
    authServerUrl: 'https://xx/getStsToken',
    fetcher: (authServerUrl) async {
      return UserApi.builder().ossToken();
    },
  );
  OSSClient _client;

  final LoadingDialogController loadingDialogController =
      LoadingDialogController();

  final double iconSize = 28.h;
  final int maxAssetsLength = 9;
  final Set<AssetEntity> failedAssets = <AssetEntity>{};
  final List<CancelToken> assetsUploadCancelTokens = <CancelToken>[];
  final Map<AssetEntity, String> uploadedAssetId = <AssetEntity, String>{};

  List<AssetEntity> selectedAssets = <AssetEntity>[];

  bool isLoading = false;

  bool isAssetListViewCollapsed = false;
  int uploadedAssets = 0;
  int uploadedPosts = 0;
  List<String> uploadAssetsKey = [];
  double maximumKeyboardHeight = EmotionPad.emoticonPadDefaultHeight;

  int get imagesLength => selectedAssets.length;

  bool get hasImages => selectedAssets.isNotEmpty;

  @override
  void initState() {
    super.initState();
    _client = OSSClient(
      endpoint: 'oss-cn-hangzhou.aliyuncs.com',
      credentialProvider: _provider,
    );
  }

  /// Method to pick assets using photo selector.
  /// 使用图片选择器选择图片
  Future<void> pickAssets() async {
    if (await Permission.accessMediaLocation.request().isGranted) {
      final List<AssetEntity> result = await AssetPicker.pickAssets(
        context,
        selectedAssets: selectedAssets,
        themeColor: currentThemeColor,
      );
      if (result != selectedAssets && result != null) {
        selectedAssets = List<AssetEntity>.from(result);
        if (mounted) {
          setState(() {});
        }
      }
    }
  }

  /// Reverse [isAssetListViewCollapsed] state.
  /// 切换资源列表展开收起
  void switchAssetsListCollapse() {
    setState(() {
      isAssetListViewCollapsed = !isAssetListViewCollapsed;
    });
  }

  /// Check whether there's content left when trying to pop.
  /// 返回时检查是否有未发送的内容
  Future<bool> isContentEmptyWhenPop() async {
    if (imagesLength != 0) {
      final bool confirm = await ConfirmationDialog.show(
        context,
        title: '退出发布',
        content: '仍有未发送的内容，是否退出？',
        showConfirm: true,
      );
      if (confirm) {
        NavigatorUtils.ThrioNavigatorPop();
        return true;
      }
      return false;
    } else {
      NavigatorUtils.ThrioNavigatorPop();
      return true;
    }
  }

  /// Check if the content is empty.
  /// 检查内容是否为空
  void checkContentEmptyWhenPublish() {
    if (imagesLength == 0) {
      showCenterToast('不能为空');
    } else {
      checkConvention();
    }
  }

  /// Check if user confirmed with convention.
  /// 检查用户是否同意了公约
  Future<void> checkConvention() async {
    final bool confirm = await ConventionDialog.show(context);
    if (confirm) {
      LoadingDialog.show(
        context,
        controller: loadingDialogController,
        text: hasImages
            ? '正在上传图片 (${uploadedAssets + 1}/$imagesLength)'
            : '正在发布...',
      );
      setState(() {
        isLoading = true;
      });
      if (hasImages) {
        runImagesRequests();
      } else {
        unawaited(runPublishRequest());
      }
    }
  }

  /// Execute images upload requests.
  /// 执行图片上传请求
  ///
  /// This method doesn't required to be [Future],
  /// just run them with [Iterable.forEach] and using [CancelToken] (Completer)
  /// to control requests' cancel when one of them failed.
  /// 该方法不需要声明为 [Future]，只需要使用 forEach 调用异步方法，
  /// 并且使用 [CancelToken] 来控制 请求。
  /// 为了避免过多状态导致的意外结果，当任意资源上传失败时，就取消所有请求，要求用户处理。
  void runImagesRequests() {
    setState(() {
      failedAssets.clear();
    });

    /// Using `forEach` instead of `for in` is that `for in` will execute
    /// one by one, and stuck if the previous request takes a long duration.
    /// `forEach` will send requests at the same time.
    /// 使用`forEach`而不是`for in`是因为`for in`会逐个执行，
    /// 如果上一个请求耗费了很长时间，整个流程都将被 阻塞，
    /// 而使用`forEach`会同时发起所有请求。
    selectedAssets.forEach(assetsUploadRequest);
  }

  static const int IMAGE_MAX_SIZE = 1024;

  Future<void> assetsUploadRequest(AssetEntity asset) async {
    /// Make a data record first, in order to keep the sequence of the images.
    /// 先创建数据条目，保证上传的图片的顺序。
    // uploadedAssetId[asset] = null;
    final CancelToken cancelToken = CancelToken();
    assetsUploadCancelTokens.add(cancelToken);
    var size = asset.size;
    var tHeight = size.height;
    var tWidth = size.width;
    var bytes;
    if(tHeight > IMAGE_MAX_SIZE || tWidth > IMAGE_MAX_SIZE){
      double tScaleWidth = tWidth * 1.0 / IMAGE_MAX_SIZE;
      double tScaleHeight = tHeight * 1.0 / IMAGE_MAX_SIZE;
      double tScale = tScaleWidth > tScaleHeight ? tScaleWidth : tScaleHeight;
      tScale = tScale >= 1 ? tScale : 1;
      bytes = await asset.thumbDataWithSize(tWidth~/tScale, tHeight~/tScale);
    }else{
      bytes = await asset.originBytes;
    }

    trueDebugPrint("flutterting upload image" + bytes.length.toString());

    // final FormData formData = await PostApi.createPostImageUploadForm(asset);
    try {
      var result = await _client.postObject(
          bytes, 'quhui123', "card/" + DateUtil.getNowDateMs().toString() + asset.title);
      if (result is OSSResponse) {
        trueDebugPrint("flutterting upload image" + result.toString());
        uploadedAssetId[asset] = result.fileKey.toString();
        ++uploadedAssets;
        loadingDialogController.updateText(
          '正在上传图片'
          '(${math.min(uploadedAssets + 1, imagesLength)}/$imagesLength)',
        );

        /// Execute publish when all assets were upload.
        /// 所有图片上传完成时进行发布
        if (uploadedAssets == imagesLength) {
          unawaited(runPublishRequest());
        }
      } else {
        throw Error.safeToString('Asset ${asset.id} upload failed');
      }
    } catch (e, stack) {
      trueDebugPrint("flutterting upload image error" + stack.toString());
      isLoading = false; // 停止Loading
      uploadedAssets = 0; // 上传清零
      failedAssets.add(asset); // 添加失败entity
      loadingDialogController.changeState('failed', '图片上传失败');

      /// Cancel all request and clear token list.
      /// 取消所有的上传请求并清空所有cancel token
      assetsUploadCancelTokens
        ..forEach((CancelToken token) => token?.cancel())
        ..clear();

      if (mounted) {
        setState(() {});
      }

      trueDebugPrint('Error when trying upload images: $e');
      if (e is DioError) {
        trueDebugPrint('${e.response}');
      }
      trueDebugPrint('Images requests will be all cancelled.');
    }
  }

  //每一张图片都是一个sheet对象
  Future<void> runPublishRequest() async {
    try {
      var list = uploadedAssetId.values.toList();
      for (; uploadedPosts < list.length; uploadedPosts++) {
        PostCreateBean bean = PostCreateBean(
          categoryIds: [],
          disallowComment: false,
          interestId: widget.interestId??0,
          metaDescription: "",
          metas: [],
          originalContent: "",
          password: "",
          slug: "",
          status: "DRAFT",
          //直接发布使用PUBLISHED
          summary: "",
          tagIds: [],
          thumbnail: getImagePath(list[uploadedPosts]),
          title: "趣图",
          topPriority: 0,
          userId: currentUserId,
        );
        trueDebugPrint("flutterting create post success" + jsonEncode(bean));
        var postDetailBean = await PostApi.builder().publishSheet(bean);
      }

      if (uploadedPosts == uploadedAssetId.length) {
        loadingDialogController
            .changeState('success', '发布成功', duration: 3.seconds, customPop: () {
          selectedAssets.clear();
          Navigator.pop(context);
          NavigatorUtils.ThrioNavigatorPop();
        });
        isLoading = false;
        if (mounted) {
          setState(() {});
        }
      }
    } catch (e, stack) {
      trueDebugPrint("flutterting create post error" + stack.toString());
      loadingDialogController.changeState('failed', '发布失败');
      isLoading = false; // 停止Loading
      uploadedPosts = 0; // 上传清零
      if (mounted) {
        setState(() {});
      }
    }
  }

  String getImageTag(String value) =>
      "<img src='https://quhui123.oss-cn-hangzhou.aliyuncs.com/${value}!80q'></img>";

  String getImagePath(String value) =>
      "https://quhui123.oss-cn-hangzhou.aliyuncs.com/${value}!80q";

////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////// Just a line breaker ////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////

  /// Publish button.
  /// 发布按钮
  Widget get publishButton => MaterialButton(
        color: currentThemeColor,
        minWidth: 90.w,
        height: 30.h,
        padding: EdgeInsets.all(5),
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(13.w),
        ),
        materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
        child: Row(
          mainAxisSize: MainAxisSize.min,
          children: <Widget>[
            Padding(
              padding: EdgeInsets.only(right: 6.w),
              child: Icon(
                MIcon.send,
                size: 14,
              ),
            ),
            Text(
              '发趣图',
              style: TextStyle(
                color: Colors.white,
                fontSize: 15.sp,
                height: 1.24,
              ),
            ),
          ],
        ),
        onPressed: checkContentEmptyWhenPublish,
      );

  /// Selected asset image widget.
  /// 已选资源的单个图片组件
  Widget _assetWidget(int index) {
    final AssetEntity asset = selectedAssets.elementAt(index);
    return GestureDetector(
      onTap: !isAssetListViewCollapsed
          ? () async {
              final List<AssetEntity> result =
                  await AssetPickerViewer.pushToViewer(
                context,
                currentIndex: index,
                assets: selectedAssets,
                themeData: AssetPicker.themeData(currentThemeColor),
              );
              if (result != selectedAssets && result != null) {
                selectedAssets = result;
                if (mounted) {
                  setState(() {});
                }
              }
            }
          : null,
      child: RepaintBoundary(
        child: ExtendedImage(
          image: AssetEntityImageProvider(asset, isOriginal: false),
          fit: BoxFit.cover,
          borderRadius: BorderRadius.circular(10.w),
          shape: BoxShape.rectangle,
        ),
      ),
    );
  }

  /// Cover for error when there's any image failed in uploading.
  /// 图片上传失败时的错误遮罩
  Widget get uploadErrorCover => Positioned.fill(
        child: Container(
          color: Colors.white.withOpacity(0.7),
          child: Center(
            child: Icon(
              Icons.error,
              color: Colors.redAccent,
              size: 40.w,
            ),
          ),
        ),
      );

  /// The delete button for assets.
  /// 资源的删除按钮
  Widget _assetDeleteButton(int index) {
    return GestureDetector(
      onTap: () {
        setState(() {
          failedAssets.remove(selectedAssets.elementAt(index));
          selectedAssets.remove(selectedAssets.elementAt(index));
          if (imagesLength == 0) {
            isAssetListViewCollapsed = false;
          }
        });
      },
      child: Container(
        padding: EdgeInsets.symmetric(
          horizontal: 6.w,
          vertical: 2.h,
        ),
        decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(4.w),
          color: currentTheme.primaryColor.withOpacity(0.75),
        ),
        child: Text(
          '删除',
          style: TextStyle(
            color: currentTheme.iconTheme.color,
            fontSize: 14.sp,
            fontWeight: FontWeight.normal,
          ),
        ),
      ),
    );
  }

  /// Item shown when selected assets not reached maximum images length yet.
  /// 已选中图片数量未达到最大限制时，显示添加item。
  Widget get _assetAddItem => AnimatedContainer(
        duration: kThemeAnimationDuration,
        curve: Curves.easeInOut,
        padding: EdgeInsets.symmetric(
          horizontal: 8.w,
          vertical: 16.w,
        ),
        child: AspectRatio(
          aspectRatio: 1.0,
          child: GestureDetector(
            behavior: HitTestBehavior.opaque,
            onTap: pickAssets,
            child: DecoratedBox(
              decoration: BoxDecoration(
                shape: BoxShape.rectangle,
                borderRadius: BorderRadius.circular(10.w),
                color: currentIsDark ? Colors.grey[700] : Colors.white,
              ),
              child: Icon(
                Icons.add,
                size: (isAssetListViewCollapsed ? 20 : 50).w,
              ),
            ),
          ),
        ),
      );

  /// List view for assets.
  /// 已选资源的显示列表
  Widget get assetsListView => Align(
        alignment: AlignmentDirectional.centerStart,
        child: GestureDetector(
          onTap: isAssetListViewCollapsed ? switchAssetsListCollapse : null,
          child: AnimatedContainer(
            curve: Curves.easeInOut,
            duration: kThemeAnimationDuration,
            height: selectedAssets.isNotEmpty
                ? isAssetListViewCollapsed
                    ? 72.w
                    : 140.w
                : 0.0,
            margin: EdgeInsets.all(
              isAssetListViewCollapsed ? 12.w : 0,
            ),
            decoration: BoxDecoration(
              borderRadius: BorderRadius.circular(
                isAssetListViewCollapsed ? 15.w : 0,
              ),
              color: currentTheme.canvasColor,
            ),
            child: ListView.builder(
              shrinkWrap: isAssetListViewCollapsed,
              physics: const BouncingScrollPhysics(),
              padding: EdgeInsets.symmetric(
                horizontal: 8.w,
              ),
              scrollDirection: Axis.horizontal,
              itemCount: math.min(
                  isAssetListViewCollapsed ? imagesLength : imagesLength + 1,
                  maxAssetsLength),
              itemBuilder: (BuildContext _, int index) {
                if (index == imagesLength) {
                  return _assetAddItem;
                }
                return Padding(
                  padding: EdgeInsets.symmetric(
                    horizontal: 8.w,
                    vertical: 16.w,
                  ),
                  child: AspectRatio(
                    aspectRatio: 1.0,
                    child: Stack(
                      children: <Widget>[
                        Positioned.fill(child: _assetWidget(index)),
                        if (failedAssets
                            .contains(selectedAssets.elementAt(index)))
                          uploadErrorCover,
                        if (!isAssetListViewCollapsed)
                          Positioned(
                            top: 6.w,
                            right: 6.w,
                            child: _assetDeleteButton(index),
                          ),
                      ],
                    ),
                  ),
                );
              },
            ),
          ),
        ),
      );

  /// Button wrapper for the toolbar.
  /// 工具栏按钮封装
  Widget _toolbarButton({
    VoidCallback onPressed,
    IconData icon,
    Widget child,
    Color color,
  }) {
    return IconButton(
      padding: EdgeInsets.zero,
      onPressed: onPressed,
      icon: child ??
          Icon(
            icon,
            color: color ?? currentTheme.iconTheme.color,
            size: iconSize,
          ),
    );
  }

  /// Toolbar for the page.
  /// 工具栏
  Widget toolbar(BuildContext context) {
    return Container(
      margin: EdgeInsets.only(
        bottom: 0.0,
      ),
      height: 60.h,
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceEvenly,
        children: <Widget>[
          _toolbarButton(
            onPressed: () {
              if (imagesLength > 0) {
                switchAssetsListCollapse();
              } else {
                pickAssets();
              }
            },
            icon: imagesLength > 0
                ? Icons.photo_library
                : Icons.add_photo_alternate,
            color: !isAssetListViewCollapsed && imagesLength > 0
                ? currentThemeColor
                : currentTheme.iconTheme.color,
          ),
        ],
      ),
    );
  }

  @override
  @mustCallSuper
  Widget build(BuildContext context) {
    super.build(context);
    return WillPopScope(
      onWillPop: isContentEmptyWhenPop,
      child: FixedAppBarWrapper(
        appBar: FixedAppBar(
          backgroundColor: QuhuiColors.mainBackgroundColor,
          actions: <Widget>[publishButton],
          actionsPadding: EdgeInsets.only(right: 10.w),
          leading: MyBackButton(),
        ),
        body: Scaffold(
          backgroundColor: QuhuiColors.mainBackgroundColor,
          body: Column(
            children: <Widget>[
              if (selectedAssets.isNotEmpty) assetsListView,
              toolbar(context),
            ],
          ),
        ),
      ),
    );
  }

  @override
  bool get wantKeepAlive => true;
}
